﻿#region License

// Copyright (c) 2011, Matt Holmes
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification, are permitted provided 
// that the following conditions are met:
// 
// * Redistributions of source code must retain the above copyright notice, this list of conditions and the 
//   following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
//   the following disclaimer in the documentation and/or other materials provided with the distribution.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
// OF THE POSSIBILITY OF SUCH DAMAGE.

#endregion

using System;
using System.IO;
using System.Windows;
using IniParser;
using Microsoft.Practices.Prism.Events;
using Microsoft.Win32;

namespace SkyrimTweaker.Common.Services {
	public interface IIniService {
		event EventHandler DataDirtyChanged;
		Exception LastError { get; }
		bool LoadIniFile ();
		SkyrimData LoadedData { get; }
		bool SaveIniFile ();
	}

	internal class IniService : IIniService {
		#region Fields

		private readonly SkyrimData _data = new SkyrimData ();
		private readonly IEventAggregator _eventAggregator;
		private IniData _iniData;
		private string _iniFile;
		private Exception _lastError;
		private FileIniDataParser _parser;

		#endregion

		#region Constructors

		public IniService (IEventAggregator eventAggregator) {
			_eventAggregator = eventAggregator;
			_data.PropertyChanged += (o, e) => {
				if (e.PropertyName == "IsDirty") {
					OnDataDirtyChanged ();
				}
			};
		}

		#endregion

		#region Private Methods

		private static string FindSkyrimIni () {
			var iniPath = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), @"My Games\Skyrim\SkyrimPrefs.ini");
			if (!File.Exists (iniPath)) {
				var dialog = new OpenFileDialog { Title = "Please Locate Your Skyrem Preferences File (SkyrimPrefs.ini)", Filter = "Skyrim Preferences|SkyrimPrefs.ini", Multiselect = false };
				var flag = dialog.ShowDialog ();
				if (!flag.Value) {
					throw new FileNotFoundException ("Could not find SkyrimPrefs.ini");
				}

				iniPath = dialog.FileName;
			}

			return iniPath;
		}

		private void BackupIniFile (string iniFile) {
			var newName = "SkyrimPrefsBackup_" + DateTime.Now.ToString ("yyyyMMddHHmmss") + ".ini";
			try {
				File.Copy (iniFile, Path.Combine (Path.GetDirectoryName (iniFile), newName));
			}
			catch (Exception ex) {
				throw new InvalidOperationException ("Could not backup INI file", ex);
			}
		}

		private void CheckForInitialBackup (string iniFile) {
			var files = Directory.GetFiles (Path.GetDirectoryName (iniFile), "SkyrimPrefsBackup_*.ini");
			if (files.Length == 0) {
				var result = MessageBox.Show ("SkyrimTweaker has not detected any INI file backups. Would you like to make an initial backup of SkyrimPrefs.ini?", "Backup Data",
				                              MessageBoxButton.YesNo,
				                              MessageBoxImage.Question);
				if (result == MessageBoxResult.Yes) {
					BackupIniFile (iniFile);
				}
			}
		}

		private void OnDataDirtyChanged () {
			if (DataDirtyChanged != null) {
				DataDirtyChanged (this, EventArgs.Empty);
			}
		}

		private void ParseIniFile (string iniFile) {
			try {
				_parser = _parser ?? (new FileIniDataParser ());
				_iniData = _parser.LoadFile (iniFile, true);
				_data.ReadIniData (_iniData);
			}
			catch (Exception ex) {
				throw new InvalidOperationException ("Error reading INI file", ex);
			}
		}

		#endregion

		#region IIniService Members

		public bool LoadIniFile () {
			try {
				_iniFile = FindSkyrimIni ();
				CheckForInitialBackup (_iniFile);
				ParseIniFile (_iniFile);
				return true;
			}
			catch (Exception ex) {
				_lastError = ex;
				return false;
			}
		}

		public bool SaveIniFile () {
			try {
				var result = MessageBox.Show ("Would you like to backup your preferences before saving?", "Backup Data", MessageBoxButton.YesNo, MessageBoxImage.Question);
				if (result == MessageBoxResult.Yes) {
					BackupIniFile (_iniFile);
				}

				_data.WriteIniData ();
				// Drop the global section crap, we don't want it
				if (_iniData.Sections.ContainsSection (IniData.GlobalSectionName)) {
					_iniData.Sections.RemoveSection (IniData.GlobalSectionName);
				}

				_parser.SaveFile (_iniFile, _iniData);
				return true;
			}
			catch (Exception ex) {
				_lastError = ex;
				return false;
			}
		}

		public SkyrimData LoadedData {
			get { return _data; }
		}

		public Exception LastError {
			get { return _lastError; }
		}

		public event EventHandler DataDirtyChanged;

		#endregion
	}
}
